#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2021/6/3 22:48
# @Author  : huidong.bai
# @File    : run.py
# @Software: PyCharm

import sys

from ui.main import Ui_Form

from PyQt5.QtWidgets import QWidget, QApplication, QPushButton, QTreeWidgetItem, QMessageBox, QFileDialog, QGraphicsOpacityEffect
from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import QTimer

from lib.SwitchButton import SwitchButton
from lib.adb_utils import get_sn
import os
from threading import currentThread, Thread
import pyqtgraph as pg
from audio_controller.audio_controller import query_to_record

import wave
import pylab as pl
import numpy as np
from pyqtgraph import mkPen

# 定义全局线程类
thread = None
t = None
time_result = []


class MyWindow(QWidget):
    log_print = pyqtSignal(str)
    record_info_signal = pyqtSignal(str)
    add_tree_audio_list_signal = pyqtSignal(float, int, str, str)

    def __init__(self):
        super().__init__()
        self.timer = QTimer()
        self.switch = SwitchButton(54, 27)
        self.ui = Ui_Form()
        self.ui.setupUi(self)
        self.speaker_img_display()
        self.op = QGraphicsOpacityEffect()
        self.init()

    def init(self):
        self.ui.verticalLayout_2.addWidget(self.switch)
        # self.timer.timeout.connect(self.monitor)
        # self.timer.start(2000)
        self.switch.clicked.connect(self.task)

        # 小波阈值设置条
        self.ui.horizontalSlider.setRange(0, 100)
        self.ui.horizontalSlider.valueChanged.connect(self.set_xiaobo_value)
        self.ui.horizontalSlider.setValue(30)

        # 打印日志事件
        self.log_print.connect(self.add_speaker_log)

        # TODO lamdal表达式传递路径
        # 绘制波形图按钮事件
        self.ui.pushButton_repaint.clicked.connect(self.wav_paint)

        # 切换tab事件
        # self.ui.tabWidget.currentChanged.connect(self.tab_changed)

        # 设置树控件的样式
        # 设置两列
        self.ui.treeWidget_audio_list.setColumnCount(3)
        self.ui.treeWidget_audio_list.setHeaderLabels(['序号', '文件名', '路径'])
        self.ui.treeWidget_audio_list.hideColumn(2)
        self.ui.treeWidget_audio_list.clicked.connect(self.wav_paint)

        # 隐藏正在执行的query信息
        self.op.setOpacity(0)
        self.ui.label_query_doing.setGraphicsEffect(self.op)


        # 波形图显示网格
        self.ui.PlotWidget_Waveform.showGrid(True, True)

        # 添加query_list路径
        self.ui.pushButton_addQuery.clicked.connect(self.onclicked_add_audio_path)

        # 添加唤醒音频文件
        self.ui.pushButton_addWakeup.clicked.connect(self.onclicked_add_audio_path)

        # 开始按钮事件
        self.ui.pushButton_beginTest.clicked.connect(self.onclicked_begin_test)

        # 显示录音进度
        self.record_info_signal.connect(self.show_record_info)

        # 添加树节点
        self.add_tree_audio_list_signal.connect(self.add_tree_audio_list)

        # 停止按钮操作
        self.ui.pushButton_stop.clicked.connect(self.stop_record)

    # 设别监听器待完善
    # TODO 需要处理拔掉之后激活timer的操作
    def monitor(self):
        sn = get_sn()
        if sn:
            self.ui.plainTextEdit_runtime_log.appendHtml('<font color="green">已插入设备：</font>%s' % sn)
            self.timer.stop()
        else:
            self.ui.plainTextEdit_runtime_log.appendHtml('<font color="red">未插入任何设备，请检查！</font>')

    # 获取音箱设备类型并显示对应图片
    # TODO 获取设备类型提取出来
    def speaker_img_display(self):
        speaker_type = 1
        if speaker_type:
            self.ui.label_speaker_img.setPixmap(QPixmap('./image/nano.png'))

    # 打印日志触发器
    def task(self, status):
        def thread_func():
            buffer = os.popen('adb logcat')
            t = currentThread()
            while getattr(t, "open", True):
                line = buffer.readline()
                if line:
                    self.log_print.emit(line.replace('\n', '').strip())

        global thread
        if status:
            thread = Thread(target=thread_func)
            thread.start()
        else:
            if thread is None:
                return
            thread.open = False

    # 运行日志打印事件
    def add_speaker_log(self, log):
        self.ui.plainTextEdit_speaker_log.appendPlainText(log)

    # 选项卡切换事件
    def tab_changed(self):
        # 切换到波形图，初始化操作
        if self.ui.tabWidget.currentIndex() == 3:
            self.ui.treeWidget_audio_list.clear()

            record_wav_list = os.listdir('./record_audio')
            for i in range(0, len(record_wav_list)):
                root = QTreeWidgetItem(self.ui.treeWidget_audio_list)
                root.setText(0, str(i + 1))
                root.setText(1, record_wav_list[i])
        else:
            print('没有切换到波形图')

    # 按钮事件
    def wav_paint(self):
        try:
            # 获取左边树控件返回的
            record_path = self.ui.treeWidget_audio_list.selectedItems()[0].text(2)
        except Exception as e:
            msg_box = QMessageBox(QMessageBox.Warning, 'Warning', '请选择音频!')
            msg_box.exec_()

        # 先清空一次
        self.ui.PlotWidget_Waveform.clear()

        # 小波阀值获取
        T = self.ui.horizontalSlider.value()

        # 通过阀值生成获取波形
        X, Y = self.audio_data(T, record_path)

        # 绘制原生
        self.ui.PlotWidget_Waveform.plot(X, Y, pen=mkPen('#43CD80'))

        # 绘制关注波形
        self.data_clear(X, Y)

    # 读取波形数据
    def audio_data(self, t, file_path):
        file = wave.open(file_path, "rb")

        audio_params = file.getparams()
        nchannels, sampwidth, framerate, nframes = audio_params[:4]
        # 读取波形数据
        str_data = file.readframes(nframes)
        file.close()

        # 将波形数据转换为数组
        wave_data = np.frombuffer(str_data, dtype=np.short)

        wave_data = np.array(wave_data)

        # 设置数组的形状
        wave_data.shape = -1, nchannels
        wave_data = wave_data.T

        # 小波阀值去白噪，获取最终Y轴波形数组
        wave_data[abs(wave_data) < t] = 0

        # 获取X轴时间数组
        times = np.arange(0, nframes) * (1.0 / framerate)

        return times, wave_data[0]

    # 0数组检测
    def zero_runs(self, a):
        iszero = np.concatenate(([0], np.equal(a, 0).view(np.int8), [0]))
        absdiff = np.abs(np.diff(iszero))
        ranges = np.where(absdiff == 1)[0].reshape(-1, 2)
        return ranges

    # clear数据清洗
    def data_clear(self, times, wave_data):
        global time_result
        time_result = []
        self.ui.lineEdit_wakeup_time.clear()
        self.ui.lineEdit_response_time.clear()

        self.ui.label_seccess_tag.setVisible(False)
        self.ui.label_fail_tag.setVisible(False)
        result = self.zero_runs(wave_data)
        for i in result:
            time_tmp = i[1] - i[0]
            # 超过0.25秒则记录数值，44100一秒
            if time_tmp > 11025:
                check_data = np.zeros(time_tmp, dtype=int)
                check_time = times[i[0]:i[1]]
                self.ui.PlotWidget_Waveform.plot(check_time, check_data, pen=mkPen('r'))

                # 设置绘图区域
                region = pg.LinearRegionItem()
                self.ui.PlotWidget_Waveform.addItem(region, ignoreBounds=True)
                # 设置绘图区域宽度
                region.setRegion([times[i[0]], times[i[1] - 1]])
                # 设置绘图区域不可移动
                region.setMovable(False)

                pos_x = (times[i[1] - 1] - times[i[0]]) / 2 + times[i[0]]

                show_times = format(time_tmp / 44100, '.5f')

                time_result.append(show_times)

                # 绘制文字
                text = pg.TextItem(
                    html=f'<div style="text-align: center"><span style="color: #FFF;">耗时时长</span><br><span style="color: #00ff00; font-size: 10pt;">{show_times}</span></div>',
                    anchor=(-0.3, 0.5), angle=45, border='w', fill=(0, 0, 255, 100))

                text.setPos(pos_x, 0)

                # 绘制箭头
                arrow = pg.ArrowItem(pos=(pos_x, 0), angle=-45)

                self.ui.PlotWidget_Waveform.addItem(text)
                self.ui.PlotWidget_Waveform.addItem(arrow)

        # 结果输出
        if len(time_result) == 3:
            self.ui.lineEdit_wakeup_time.setText(time_result[0])
            self.ui.lineEdit_response_time.setText(time_result[-1])
            self.ui.label_seccess_tag.setVisible(True)
        else:
            self.ui.label_fail_tag.setVisible(True)

    # 设置小波阀值文本
    def set_xiaobo_value(self):
        slider = self.sender()
        self.ui.label_xiaoboValue.setText(str(slider.value()))

    # 添加query路径
    def onclicked_add_audio_path(self):
        obj_name = self.sender().objectName()
        desktop_path = os.path.join(os.path.expanduser("~"), "Desktop")
        if obj_name == 'pushButton_addQuery':
            audio_path = QFileDialog.getExistingDirectory(self, "选取Query音频路径", desktop_path)
            self.ui.lineEdit_queryAudio_path.setText(audio_path)
        elif obj_name == 'pushButton_addWakeup':
            wakeup_path, ok = QFileDialog.getOpenFileName(self, "选取唤醒音频文件", desktop_path, "Wav Files (*.wav)")
            self.ui.lineEdit_wakeupAudio_path.setText(wakeup_path)

    # 开始事件槽函数
    def onclicked_begin_test(self):
        file_path = self.ui.lineEdit_queryAudio_path.text()
        wakeup_path = self.ui.lineEdit_wakeupAudio_path.text()
        if file_path and wakeup_path:
            audio_list = os.listdir(file_path)
            query_list = [audio for audio in audio_list if audio.split('.')[-1] == 'wav']
            if len(query_list):
                # 显示正在执行的query信息
                self.op.setOpacity(1)
                self.ui.label_query_doing.setGraphicsEffect(self.op)

                # 按钮置灰操作
                self.ui.pushButton_beginTest.setEnabled(False)

                def task():
                    for i in range(0, len(query_list)):
                        program = 100 * (i+1)/len(query_list)
                        self.record_info_signal.emit(f'<h4>正在播放：<font color="green">{query_list[i]}</font></h4>')
                        query = os.path.join(file_path, query_list[i])
                        records_file = os.path.join(file_path, 'records')
                        record_path = os.path.join(records_file, query_list[i])
                        query_to_record(wakeup_path, query, record_path, records_file)
                        self.add_tree_audio_list_signal.emit(program, i+1, query_list[i], record_path)
                    # 按钮恢复操作
                    self.ui.pushButton_beginTest.setEnabled(True)

                global t
                t = Thread(target=task)
                t.start()
            else:
                msg_box = QMessageBox(QMessageBox.Warning, 'Warning', '没有找到任何可执行query音频，请检查!')
                msg_box.exec_()
        else:
            msg_box = QMessageBox(QMessageBox.Warning, 'Warning', '请选择需要执行的Query音频或唤醒文件!')
            msg_box.exec_()

    # 显示录音进度槽函数
    def show_record_info(self, playing_audio):
        self.ui.label_query_doing.setText(playing_audio)

    # 添加树节点槽函数
    def add_tree_audio_list(self, program, index, query, record_path):
        root = QTreeWidgetItem(self.ui.treeWidget_audio_list)
        root.setText(0, str(index))
        root.setText(1, query)
        root.setText(2, record_path)
        self.ui.progressBar_record.setValue(program)

    # 停止线程
    @staticmethod
    def stop_record():
        global t
        if t is None:
            return
        t.open = False


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyWindow()
    window.show()
    sys.exit(app.exec())
